//gcc -o vorbisstreamer main.c -lvorbis -lvorbisfile -lvorbisenc -std=c99 -ggdb `mysql_config --cflags --libs`
#include "vorbisstreamer.c"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>

#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1

#define CMD_ADDFILE 0x11111111
#define CMD_PUSHFILE 0x22222222
#define CMD_SETLOOP 0x33333333
#define CMD_SKIP 0x44444444
#define CMD_SHOWQUEUE 0x55555555
#define QueueElement void**
//Controllersocket, empfängt commands
int controller;
QueueElement queueStart=0;
QueueElement queueEnd=0;
pthread_t controlThread;
pthread_mutex_t queueMutex;

int loop=0,shuffle=0,skip=0;

void insertIntoQueue(char* buffer,QueueElement* start, QueueElement* end);
void insertBeforeQueue(char* buffer,QueueElement* start, QueueElement* end);

char* fahrstuhlmusik="fahrstuhlmusik.ogg";

static void daemonize(void)
{
    pid_t pid, sid;

    /* already a daemon */
    if ( getppid() == 1 ) return;

    /* Fork off the parent process */
    pid = fork();
    if (pid < 0) {
        exit(EXIT_FAILURE);
    }
    /* If we got a good PID, then we can exit the parent process. */
    if (pid > 0) {
        exit(EXIT_SUCCESS);
    }

    /* At this point we are executing as the child process */

    /* Change the file mode mask */
    umask(0);

    /* Create a new SID for the child process */
    sid = setsid();
    if (sid < 0) {
        exit(EXIT_FAILURE);
    }

    /* Change the current working directory.  This prevents the current
       directory from being locked; hence not being able to remove it. */
    /*if ((chdir("/")) < 0) {
        exit(EXIT_FAILURE);
    }*/

    /* Redirect standard files to /dev/null */
    freopen( "/dev/null", "r", stdin);
    freopen( "/dev/null", "w", stdout);
    freopen( "/dev/null", "w", stderr);
}

void* controlLoop(void* a)
{
	controller= socket (AF_UNIX,SOCK_STREAM,0);
	
	struct sockaddr_un local;
	local.sun_family = AF_UNIX;
	strcpy(local.sun_path, "socket");
	unlink(local.sun_path);
	int len = strlen(local.sun_path) + sizeof(local.sun_family);
	bind(controller, (struct sockaddr *)&local, len);
	listen(controller,32);
	while(1)
	{
		int command;
		int inp=0;
		inp=accept(controller,0,0);
		if(inp<0) continue;
		recv(inp,&command,sizeof(int),0);
		if(command==CMD_ADDFILE)
		{
			int length;
			recv(inp,&length,sizeof(int),0);
			fprintf(stderr,"%d",length);
			char* buffer=malloc(length+1);
			int count=0;
			while(count<length)
			{
				count+=recv(inp,buffer+count,length-count,0);
			}	
			buffer[count]=0;
			insertIntoQueue(buffer,&queueStart,&queueEnd);
		}
		else if(command==CMD_PUSHFILE)
		{
			int length;
			recv(inp,&length,sizeof(int),0);
			fprintf(stderr,"%d",length);
			char* buffer=malloc(length+1);
			int count=0;
			while(count<length)
			{
				count+=recv(inp,buffer+count,length-count,0);
			}	
			buffer[count]=0;
			insertBeforeQueue(buffer,&queueStart,&queueEnd);
		}
		else if(command==CMD_SETLOOP)
		{
			recv(inp,&loop,sizeof(int),0);	
		}
		else if(command==CMD_SKIP)
		{
			skip=1;
		}
		else if(command==CMD_SHOWQUEUE)
		{
			
			pthread_mutex_lock(&queueMutex);
			void** element=queueStart;
			while(element!=0)
			{
				char buffer[strlen(element[0])];
				strcpy(buffer,element[0]);
				strcat(buffer,"\n");
				printf("buffer: %s\n",buffer);
				send(inp,buffer,strlen(buffer),0);
				element=element[1];
			}
			pthread_mutex_unlock(&queueMutex);
		}
		close(inp);
	}
}

void insertIntoQueue(char* buffer,QueueElement* start, QueueElement* end)
{
	pthread_mutex_lock(&queueMutex);
	
	QueueElement element=malloc(sizeof(void*)*2);
	element[0]=(void*)buffer;
	element[1]=0;
	if(*start==0)
	{
		*start=element;
		*end=element;
	}
	else
	{
		(*end)[1]=element;
		*end=element;
	}
	pthread_mutex_unlock(&queueMutex);	
}

void insertBeforeQueue(char* buffer,QueueElement* start, QueueElement* end)
{
	pthread_mutex_lock(&queueMutex);
	QueueElement element=malloc(sizeof(void*)*2);
	element[0]=(void*)buffer;
	element[1]=*start;
	if(*start==0)
	{
		*start=element;
		*end=element;
	}
	else
	{
		*start=element;
	}
	pthread_mutex_unlock(&queueMutex);	
}

//returns first song. remember to dealloc the buffer
char* getFirstSong(QueueElement* start, QueueElement* end)
{
	pthread_mutex_lock(&queueMutex);
	if(*start==0)
	{
		pthread_mutex_unlock(&queueMutex);
		return 0;
	}
		
	
	char* buffer=(char*)*start[0];
	//nur ein element
	if(*end==*start)
	{
		free(*start);
		*start=0;
		*end=0;
		
	}
	//mehrere elemente
	else
	{
		QueueElement old=*start;
		*start=old[1];
		free(old);		
	}
	pthread_mutex_unlock(&queueMutex);
	return buffer;
}

void debugQueue(QueueElement start, QueueElement end)
{
	void** element=start;
	while(element!=0)
	{
		printf("%s\n",(char*)element[0]);
		element=element[1];
	}
}

int connectToServer()
{
	int commander=socket(AF_UNIX,SOCK_STREAM,0);
	struct sockaddr_un local;
	local.sun_family = AF_UNIX;
	strcpy(local.sun_path, "socket");
	int len = strlen(local.sun_path) + sizeof(local.sun_family);
	if(connect(commander,(struct sockaddr *)&local,len)==-1)
		perror("could not establish a connection...");
	return commander;
}

void addTrack(char* url)
{
	int commander=connectToServer();
	int cmdlen=sizeof(char)*strlen(url)+sizeof(int)*2;
	char* command=malloc(cmdlen);
	((int*)command)[0]=CMD_ADDFILE;
	((int*)command)[1]=strlen(url);
	strcpy(command +sizeof(int)*2/sizeof(char),url);
	send(commander,command, cmdlen,0);
}

void pushTrack(char* url)
{
	int commander=connectToServer();
	int cmdlen=sizeof(char)*strlen(url)+sizeof(int)*2;
	char* command=malloc(cmdlen);
	((int*)command)[0]=CMD_PUSHFILE;
	((int*)command)[1]=strlen(url);
	strcpy(command +sizeof(int)*2/sizeof(char),url);
	send(commander,command, cmdlen,0);
}

void setLoop(char* arg)
{
	int commander=connectToServer();
	int cmdlen=sizeof(int)*2;
	char* command=malloc(cmdlen);
	((int*)command)[0]=CMD_SETLOOP;
	if(arg[0]=='1')
		((int*)command)[1]=1;
	else	
		((int*)command)[1]=0;
	send(commander,command, cmdlen,0);
}

void skipTrack()
{
	int commander=connectToServer();
	int cmdlen=sizeof(int);
	char* command=malloc(cmdlen);
	((int*)command)[0]=CMD_SKIP;
	send(commander,command, cmdlen,0);
}

void outputQueue()
{
	int commander=connectToServer();
	int cmdlen=sizeof(int);
	char* command=malloc(cmdlen);
	((int*)command)[0]=CMD_SHOWQUEUE;
	send(commander,command, cmdlen,0);
	char buffer[1024];
	int size=0;
	while((size=recv(commander,buffer,1024,0))>0)
	{
		char str[size+1];
		str[size]=0;
		strncpy(str,buffer,size);
		printf("%s",str);
	}
}

void outputHelp()
{
	printf("Vorbisstreamer - by Lukas Pfahler\n\n");
	printf("Usage: vorbisstreamer command [parameters]\n\n");
	printf("List of commands:\n");
	printf("  start - start vorbisstreamer in terminal\n");
	printf("  daemon - starts vorbisstreamer in daemon mode\n");
	printf("  add file#1 .. file#n - enqueues files into the playback queue\n");
	printf("  push file#1 .. file#n - inserts files in front of the playback queue\n");
	printf("  play file#1 .. file#n - starts playing the files\n");
	printf("  skip - plays the next track\n");
	printf("  loop 0/1 - enables/disables looping\n");
	printf("  queue - displays the playback queue\n");
	printf("  help - displays this message\n");
	printf("\n\nThanks for using vorbisstreamer\n \nLukas Pfahler \n\n");
}

/*this is where the magic happens :D*/
int main (int argc,char **argv)
{
	int start=0,daemon=0;
    if(argc<=1)
	{
		outputHelp();
	}
	else
	{
		if(strcmp(argv[1],"add")==0)
		{
			for(int i=2;i<argc;i++)
			{
				addTrack(argv[i]);
			}
		}
		else if(strcmp(argv[1],"push")==0)
		{
			for(int i=2;i<argc;i++)
			{
				pushTrack(argv[i]);
			}
		}
		else if(strcmp(argv[1],"play")==0)
		{
			for(int i=2;i<argc;i++)
			{
				pushTrack(argv[i]);
			}
			skipTrack();
		}
		else if(strcmp(argv[1],"loop")==0)
		{
			setLoop(argv[2]);
		}
		else if(strcmp(argv[1],"start")==0)
		{
			start=1;
		}
		else if(strcmp(argv[1],"daemon")==0)
		{
			start=1;
			daemon=1;
		}
		else if(strcmp(argv[1],"idle")==0)
		{
			//fahrstuhlmusik setzen
		}
		else if(strcmp(argv[1],"skip")==0)
		{
			skipTrack();
		}
		else if(strcmp(argv[1],"queue")==0)
		{
			outputQueue();
		}
		else if(strcmp(argv[1],"help")==0)
			{
				outputHelp();
			}
	}
	if(daemon) daemonize();
	if(start)
	{
		signal(SIGABRT,crashServer);
		signal(SIGTERM,crashServer);
		signal(SIGINT,crashServer);
		fprintf(stderr,"\nVorbisStreamer by Lukas Pfahler \n");
		fprintf(stderr,"\n%sVorbisStreamer initialized.\n",timestamp());
	
		launchServer(0.6);
		pthread_create(&controlThread,0,&controlLoop,0);
		//outputHeader();
		while(1)
		{
			char* file=getFirstSong(&queueStart,&queueEnd);
			skip=0;
			if(file==0)
			{
				streamFile(fahrstuhlmusik,&skip);
			}
			else
			{	

				streamFile(file,&skip);
				if(!loop)
					free(file);
				else
					insertIntoQueue(file,&queueStart,&queueEnd);
			}
			
		}
		closeServer();
	}
	return 0;
}

